﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using App.Model;
using App.Model.Main;
using App.Utility;
using EAF.Utility;
using EAF.Web;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace App.Admin
{
    public class Startup
    {

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
            //设置静态配置实例
            AppConfig.SetConfiguration(configuration);
        }

        public IConfiguration Configuration { get; }

        ///// <summary>
        ///// 聊天室
        ///// </summary>
        //public static ImChat ImChat1 { get; set; }

        //接口文档版本号 不要修改
        private const string apiDocVersion = "v1.0";

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();

            #region 注入配置

            //注册HttpContextAccessor
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            //注册数据库上下文
            services.AddScoped<AppMainContext>();
            services.AddScoped<AppLogContext>();

            ////注册聊天功能
            //services.AddSingleton<IComWebSocket, ImChat>();

            //注册程序内置缓存
            services.AddSingleton<IMemoryCache>(factory =>
            {
                var cache = new MemoryCache(new MemoryCacheOptions());
                return cache;
            });

            services.AutoDIServices("App.IService", "App.Service");

            #endregion

            #region 接口文档

            services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc(apiDocVersion, new OpenApiInfo
                {
                    Version = apiDocVersion,
                    Title = "App.Admin文档",
                    Description = "一键生成CURD接口和视图"
                });
                //c.OrderActionsBy(o => o.RelativePath); 

                //加载xml文档
                var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);    //获取应用程序所在目录（绝对，不受工作目录影响，建议采用此方法获取路径）
                var xmlPath = Path.Combine(basePath, typeof(Program).Namespace + ".xml");   //生成里设置的xml文件名
                options.IncludeXmlComments(xmlPath, true);
                var dtoXmlPath = Path.Combine(basePath, typeof(App.DTO.DtoPageReqBase).Namespace + ".xml");   //生成里设置的xml文件名
                options.IncludeXmlComments(dtoXmlPath);
                var appUtilityXmlPath = Path.Combine(basePath, typeof(App.Utility.AppData).Namespace + ".xml");   //生成里设置的xml文件名
                options.IncludeXmlComments(appUtilityXmlPath);
                //options.DocumentFilter<HiddenApiFilter>();

                #region 设置授权按钮

                //添加Jwt验证设置
                options.AddSecurityRequirement(new OpenApiSecurityRequirement()
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Id = "EFA_Token",
                                Type = ReferenceType.SecurityScheme
                            }
                        },
                        new List<string>()
                    }
                });
                options.AddSecurityDefinition("EFA_Token", new OpenApiSecurityScheme
                {
                    Description = "请在/BasePlatform/SysLogin/Token接口输入登录名和密码执行 将返回的token填写到下面的输入框",
                    Name = "token",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.ApiKey
                });

                #endregion

            });

            #endregion

            #region 跨域方案

            services.AddCors(options =>
            {
                options.AddPolicy("Limit", policy =>
                {
                    policy
                    //.WithOrigins(_appConfig.CorUrls)
                    .SetIsOriginAllowed((url) => true)
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
                });

                /*
                //浏览器会发起2次请求,使用OPTIONS发起预检请求，第二次才是api异步请求
                options.AddPolicy("All", policy =>
                {
                    policy
                    .AllowAnyOrigin()
                    .SetPreflightMaxAge(new TimeSpan(0, 10, 0))
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
                });
                */
            });

            #endregion

            #region 控制器

            services.AddControllers(options =>
            {
                //添加过滤器
                options.Filters.Add<AdminExceptionAttribute>();
                //options.Filters.Add<LogActionFilter>();

                //禁止去除ActionAsync后缀
                //options.SuppressAsyncSuffixInActionNames = false;
            })
            .AddNewtonsoftJson(options =>       //此处需要引入Microsoft.AspNetCore.Mvc.NewtonsoftJson
            {
                //忽略循环引用
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                //使用驼峰 首字母小写
                options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
                //设置时间格式
                options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
            });

            //启用同步读取请求流
            services.Configure<Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions>(x => x.AllowSynchronousIO = true).Configure<IISServerOptions>(x => x.AllowSynchronousIO = true);

            #endregion

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                AdminTokenOperation.TokenValidTimeMins = 9999;
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();

            app.UseStaticFiles();

            app.UseRouting();

            //app.UseAuthorization();

            //启用跨域方案 必须在UseRouting、UseEndpoints之间启用
            app.UseCors("Limit");

            app.Use(next => context =>
            {
                //启用body流缓存 可重复读取
                context.Request.EnableBuffering();
                return next(context);
            });

            //配置imChat聊天室
            app.UseWebSockets();
            //app.Map("/ImChat", imChat.WebSocketHandle);

            //启用路由 如果其他app.xxx方法无效可尝试放置到此方法前面
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });


            #region 接口文档

            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint($"/swagger/{apiDocVersion}/swagger.json", $"App.Admin {apiDocVersion}");
                //直接根目录访问，如果是IIS发布可以注释该语句，并打开launchSettings.launchUrl
                c.RoutePrefix = "swagger";
                //折叠Api
                //c.DocExpansion(Swashbuckle.AspNetCore.SwaggerUI.DocExpansion.None);
                //不显示Models
                c.DefaultModelsExpandDepth(-1);
            });

            #endregion

            #region 映射配置

            ////全局注册实体映射基础类型转换 如时间datetime转成时间戳long 注意datetime和datetime?不同可以分开写
            //EntityTool.CreateTransType<DateTime, long>(datetime => 1);
            ////EntityTool.CreateMap<DateTime?, long>((datetime) => datetime.Value.Timestamp());
            //EntityTool.CreateTransType<DateTime?, long>((datetime) =>
            //{
            //    //时间转换为时间戳
            //    return datetime.HasValue ? datetime.Value.Timestamp() : 0;
            //});
            //EntityTool.CreateTransType<BaseNJ, Json_BaseNJ>((nj) =>
            //{
            //    //使用此方式可实现实体内所有字段及子字段绑定 递归一样的效果 反射实现效率较低 但可以灵活忽略字段
            //    //return nj.AutoBindPropertyTo(new Json_BaseNJ());
            //    //此方式本身就支持所有字段及子字段数据映射 效率高 但不支持忽略字段 如果使用此方法不需要注册类型都可以自动转换n层子字段和集合
            //    return nj.AutoMapObjectTo(new Json_BaseNJ());
            //});
            //EntityTool.CreateTransType<List<BaseNJ>, List<Json_BaseNJ>>(list =>
            //{
            //    var result = new List<Json_BaseNJ>();
            //    for (int i = 0; i < list.Count; i++)
            //    {
            //        result.Add(new Json_BaseNJ
            //        {
            //            Id = list[i].Id,
            //            NJMC = list[i].NJMC
            //        });
            //    }
            //    return result;
            //});

            #endregion

            #region 应用配置

            //启用全局使用静态HttpContext
            app.UseStaticHttpContext();
            //向通用工具类提供获取服务的功能
            app.SetServiceProvider();

            EAF.DBC.MsgHandle.Config.UseRabbitMQ = false;

            //设置基础信息
            AppData.PlatformId = 1;
            AppData.PlatformApiPrefix = "BasePlatform";
            AppData.ProjectName = "App.";
            AppConfig.DefaultPageSize = 10;
            AppConfig.DefaultDateTime = new DateTime(1900, 1, 1);


            //设置登录状态缓存前缀
            AdminTokenOperation.LoginTokenKey = RedisKey.BasePlatform_LoginToken;
            AdminTokenOperation.IsSingleLogin = false;
#if DEBUG
            AdminTokenOperation.TokenValidTimeMins = 9999;
#endif

            //初始化系统参数 建议先设置好平台等参数再执行此方法 执行成功后可读SysParam表配置 在此之前只能读json文件配置
            app.InitializeData(env);

            //其他库配置在配置表里 需要在初始化数据之后再使用
            //services.AddDbContext<DBC.DCLogContext>("SQL.Log", () =>
            //{
            //    IFreeSql fsql = new FreeSql.FreeSqlBuilder()
            //     .UseConnectionString(FreeSql.DataType.SqlServer, AppConfig.AllValue["SQL.Log"])
            //     .UseAutoSyncStructure(false)
            //     .UseNoneCommandParameter(true)
            //     .UseMonitorCommand(ExecuteSqlBefore, ExecuteSqlAfter)
            //     //.UseConfigEntityFromDbFirst(true)
            //     .Build();
            //    return fsql;
            //});

            //LogFactory.GetFileLog(this.GetType()).Debug("测试debug消息");
            //LogFactory.FLog.Debug("测试debug消息");
            //LogFactory.DLog.Custom(new RLogNormal
            //{
            //    Level = LogLevel.Error,
            //    Content = "测试内容",
            //    Description = "测试描述",
            //    CreateTime = DateTime.Now,
            //    CreaterId = 1,
            //    CreaterName = "张三"
            //});
            //LogFactory.RLog.Custom(new RLogBehavior
            //{
            //    Level = LogLevel.Error,
            //    Content = "测试内容2",
            //    Description = "测试描述",
            //    CreateTime = DateTime.Now,
            //    CreaterId = 1,
            //    CreaterName = "张三",
            //    Table = "表名"
            //}, RabbitQueue.L_业务行为, HttpContext);

            //var key = AppConfig.AllValue["FileSite.UpLoad.Ftp.AddressIP"];
            //key = AppConfig.AllValue["FileSite.UpLoad.Ftp.AddressIP"];
            //key = AppConfig.AllValue["FileSite.UpLoad.Ftp.AddressIP"];
            //key = AppConfig.AllValue["FileSite.UpLoad.Ftp.AddressIP"];

            #endregion

            #region 表结构同步
#if DEBUG

            using (var appMainContext = new AppMainContext())
            {
                //需要同步表结构执行此方法 同步完建议注释 影响启动速度
                //appMainContext.Orm.CodeFirst.SyncStructure<SysParam>();

                ////同步Model层所有继承EntityBase<int>的实体表结构 慎用
                //var types = DoRunTime.GetTypesByAssembly("App.Model").Where(t => t.BaseType == typeof(EntityBase<int>));
                //appMainContext.Orm.CodeFirst.SyncStructure(types.ToArray());


                //"Table sync finish".WriteSuccessLine();
            }

#endif
            #endregion
            // var salt2 = Guid.NewGuid().ToString().Replace("-", "");
            // var pwd2 = DoEncrypt.MD5(salt2 + 123);

            "Start Finish\n".WriteSuccessLine();
        }
    }
}
